/**
 * Copyright (c) 2015-2020, wss 王山山
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.wss.utils.scan;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;

import com.wss.utils.StringUtil;
import com.wss.utils.annotations.NotNull;

/**
 * 扫描工具类，用于扫描指定包下的类
 * 
 * @author wss
 *
 */
public final class ScanUtil {
	private static final Logger LOG = Logger.getLogger(ScanUtil.class.getName());
	private static ClassLoader CLASS_LOADER = ScanUtil.class.getClassLoader();
	private static final ScanClasses PACKAGE_SCAN_CLASSES = new PackageScanClasses();
	private static final ScanClasses JAR_SCAN_CLASSES = new JarScanClasses();
	
	static final String EXCLAMATION = "!";
	static final String CLASS_FILE_SUFFIX = ".class";
	static final String JAR_FILE_SUFFIX = ".jar";
	static final String PACKAGE_SEPARATOR_PATTERN = "\\.";
	static final String FILE_PATH_SEPARATOR = "/";
	static final String DOT = ".";
	static final String FILE_PATH_SEPARATOR_PATTERN = "\\/";
	static final int POSITION = 5;
	static final int POSITION_DISTANCE = 1;
	static final String JAR_FILE_SEPARATOR = "/";
	
	public static List<String> scanClassesUnderPackage(@NotNull String packageName) {
		Objects.requireNonNull(packageName);
		
		packageName = replaceDot(packageName);
		String filePath = getFilePath(packageName);
		if (StringUtil.isBlank(filePath)) {
			LOG.warning("Not exists package: " + packageName.replaceAll(FILE_PATH_SEPARATOR_PATTERN, DOT));
			return new ArrayList<String>(0);
		}
		if (isJarFile(filePath)) {
			return JAR_SCAN_CLASSES.scanPackage(filePath, packageName);
		}
		else {
			return PACKAGE_SCAN_CLASSES.scanPackage(filePath, packageName);
		}
	}
	
	public static List<String> scanClassesUnderPackageRecursive(@NotNull String packageName) {
		Objects.requireNonNull(packageName);
		
		packageName = replaceDot(packageName);
		String filePath = getFilePath(packageName);
		if (StringUtil.isBlank(filePath)) {
			LOG.warning("Not exists package: " + packageName.replaceAll(FILE_PATH_SEPARATOR_PATTERN, DOT));
			return new ArrayList<String>(0);
		}
		if (isJarFile(filePath)) {
			return JAR_SCAN_CLASSES.scanPackageRecursive(filePath, packageName);
		}
		else {
			return PACKAGE_SCAN_CLASSES.scanPackageRecursive(filePath, packageName);
		}
	}
	
	private static String replaceDot(String packageName) {
		return packageName.replaceAll(PACKAGE_SEPARATOR_PATTERN, FILE_PATH_SEPARATOR);
	}
	
	private static String getFilePath(String packageName) {
		URL url = CLASS_LOADER.getResource(packageName);
		if (url == null) {
			return "";
		}
		String fileUrl = url.getFile();
		int index = fileUrl.indexOf(EXCLAMATION);
		if (index != -1) {
			fileUrl = fileUrl.substring(0, index);
		}
		return fileUrl;
	}
	
	private static boolean isJarFile(String filePath) {
		return filePath.endsWith(JAR_FILE_SUFFIX);
	}
}
